#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <arpa/inet.h>

#include "socs/comm/types.h"

/* The maximum network length for IPv4. */
static const __u8 IPV4_MAX_PREFIX = 32;
/* The maximum network length for IPv6. */
static const __u8 IPV6_MAX_PREFIX = 128;

int str_to_addr4(const char *str, struct in_addr *result)
{
	if (!inet_pton(AF_INET, str, result)) {
		log_debug("Cannot parse '%s' as a IPv4 address.", str);
		return -EINVAL;
	}

	return 0;
}

int str_to_addr6(const char *str, struct in6_addr *result)
{
	if (!inet_pton(AF_INET6, str, result)) {
		log_debug("Cannot parse '%s' as a IPv6 address.", str);
		return -EINVAL;
	}

	return 0;
}
int str_to_u32(const char *str, __u32 *u32_out, __u32 min, __u32 max)
{
	long result;
	char *endptr;
	errno = 0;
	result = strtol(str, &endptr, 10);

	if (errno != 0 || str == endptr) {
		log_debug("Cannot parse '%s' as an integer value.", str);
		return -EINVAL;
	}

	if (result < min || max < result) {
		log_debug("'%s' is out of bounds (%u-%u).", str, min, max);
		return -EINVAL;
	}

	*u32_out = result;
	return 0;
}

int str_to_u64(const char *str, __u64 *u64_out, __u64 min, __u64 max)
{
	__u64 result;
	char *endptr;

	errno = 0;
	result = strtoull(str, &endptr, 10);
	if (errno != 0 || str == endptr) {
		log_debug("Cannot parse '%s' as an integer value.", str);
		return -EINVAL;
	}
	if (result < min || max < result) {
		log_debug("'%s' is out of bounds (%llu-%llu).", str, min, max);
		return -EINVAL;
	}

	*u64_out = result;
	return 0;
}


int str_to_u8(const char *str, __u8 *u8_out, __u8 min, __u8 max)
{
	__u64 result;
	int error;

	error = str_to_u64(str, &result, (__u64) min, (__u64) max);
	if (error)
		return error; /* Error msg already printed. */

	*u8_out = result;
	return 0;
}

#undef STR_MAX_LEN
#define STR_MAX_LEN (INET6_ADDRSTRLEN + 1 + 3) /* [addr + null chara] + / + pref len */
int str_to_ipv6_prefix(const char *str, struct ipv6_prefix *prefix_out)
{
	const char *FORMAT = "<IPv6 address>[/<length>] (eg. 64:ff9b::/96)";
	/* strtok corrupts the string, so we'll be using this copy instead. */
	char str_copy[STR_MAX_LEN];
	char *token;
	int error;

	if (strlen(str) + 1 > STR_MAX_LEN) {
		log_debug("'%s' is too long for this poor, limited parser...", str);
		return -EINVAL;
	}
	strcpy(str_copy, str);

	token = strtok(str_copy, "/");
	if (!token) {
		log_debug("Cannot parse '%s' as a %s.", str, FORMAT);
		return -EINVAL;
	}

	error = str_to_addr6(token, &prefix_out->address);
	if (error)
		return error;

	token = strtok(NULL, "/");
	if (!token) {
		prefix_out->len = IPV6_MAX_PREFIX;
		return 0;
	}
	return str_to_u8(token, &prefix_out->len, 0, 128); /* Error msg already printed. */
}
